/*
 * graph2D
 * Copyright (c) 2009 Shun Moriya <shun@mnu.sakura.ne.jp>
 *
 * The MIT License
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
 * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#if !defined(___GRAPH2D_WINDOW_H___)
#define ___GRAPH2D_WINDOW_H___

#include "common.h"

namespace Graph2D
{
	/*!
	 * @brief ウィンドウクラス<br/>
	 */
	template <class SUPER> class Window : public SUPER
	{
		DISALLOW_COPY_AND_ASSIGN(Window);

		//! @brief 親クラス型
		typedef SUPER super;

		//! @brief ウィンドウアニメーションタイプ
		typedef enum WindowAnimationType
		{
			WINDOW_ANIMATION_TYPE_OPEN = 0,
			WINDOW_ANIMATION_TYPE_CLOSE,
			WINDOW_ANIMATION_TYPE_OPENED,
			WINDOW_ANIMATION_TYPE_CLOSED,
		}WindowAnimationType;

	public:
		/*!
		 * @brief コンストラクタ
		 */
		Window()
			: animationType(WINDOW_ANIMATION_TYPE_CLOSED)
		{
			SUPER::setType(TYPE_WINDOW);
			SUPER::setClipping(true);
			SUPER::setVisible(false);
		}

		//! @brief デストラクタ
		virtual ~Window()
		{
		}

		/*!
		 * @brief	タイプ名を取得
		 * @return	タイプ名
		 */
		virtual const char* getTypeName() const
		{
			return "Window";
		}

		/*!
		 * @brief ウィンドウを開く
		 */
		virtual void open()
		{
			SUPER::setVisible(true);

			// アニメーション時間
			animationWindowTime = 0;

			// アニメーション開始（変更禁止）
			animationType = WINDOW_ANIMATION_TYPE_OPEN;

			// 元の色（変更禁止）
			animationWindowColor.set(0, 0, 0, 0);

			// 元のスケール（変更禁止）
			animationWindowScale.set(0, 0);
		}

		/*!
		 * @brief ウィンドウを閉じる
		 */
		virtual void close()
		{
			// アニメーション時間
			animationWindowTime = 0;

			// アニメーション開始（変更禁止）
			animationType = WINDOW_ANIMATION_TYPE_CLOSE;

			// 元の色（変更禁止）
			animationWindowColor.set(1, 1, 1, 1);

			// 元のスケール（変更禁止）
			animationWindowScale.set(1, 1);
		}

		/*!
		 * @brief	ウィンドウが閉じているか取得
		 * @return	ウィンドウが閉じているフラグ
		 */
		virtual bool closed() const
		{
			return animationType == WINDOW_ANIMATION_TYPE_CLOSED;
		}

		/*!
		 * @brief	ウィンドウがアニメーション中か取得
		 * @retval	true	ウィンドウアニメーション中
		 * @retval	false	ウィンドウアニメーション終了
		 */
		bool isAnimationStoped() const
		{
			return animationType >= WINDOW_ANIMATION_TYPE_OPENED;
		}

		/*!
		 * @brief		シリアライズイベント
		 * @param[in]	stream	mana_streamオブジェクト
		 */
		virtual void onSerialize(mana_stream* stream) const
		{
			super::onSerialize(stream);

			mana_stream_push_unsigned_char(stream, static_cast<unsigned char>(animationType));
			mana_stream_push_float(stream, animationWindowTime);
			animationWindowColor.serialize(stream);
			animationWindowScale.serialize(stream);

			mana_stream_mark(stream);
		}

		/*!
		 * @brief		デシリアライズイベント
		 * @param[in]	stream	mana_streamオブジェクト
		 */
		virtual void onDeserialize(mana_stream* stream)
		{
			super::onDeserialize(stream);

			animationType = (WindowAnimationType)mana_stream_pop_unsigned_char(stream);
			animationWindowTime = mana_stream_pop_float(stream);
			animationWindowColor.deserialize(stream);
			animationWindowScale.deserialize(stream);

			mana_stream_check(stream);
		}

		/*!
		 * @brief		更新イベント
		 * @param[in]	updateInfomation	更新情報
		 */
		virtual void onUpdate(const UpdateInfomation& updateInfomation)
		{
			if(animationType < WINDOW_ANIMATION_TYPE_OPENED)
			{
				const float normal = animationWindowTime / getAnimationTime();
				const float radian = 3.14159265f * 0.5f * normal;

				if(animationType == WINDOW_ANIMATION_TYPE_OPEN)
				{
					animationWindowColor.set(normal, normal, normal, normal);

					const float ratio = sinf(radian);
					animationWindowScale.set(ratio, ratio);
				}
				else
				{
					const float inverse = 1.0f - normal;
					animationWindowColor.set(inverse, inverse, inverse, inverse);

					const float ratio = cosf(radian);
					animationWindowScale.set(ratio, ratio);
				}

				animationWindowTime += updateInfomation.deltaTime;
				if(animationWindowTime >= getAnimationTime())
				{
					if(animationType == WINDOW_ANIMATION_TYPE_OPEN)
					{
						animationWindowColor.set(1, 1, 1, 1);
						animationWindowScale.set(1, 1);
						animationType = WINDOW_ANIMATION_TYPE_OPENED;
					}
					else
					{
						animationWindowColor.set(0, 0, 0, 0);
						animationWindowScale.set(0, 0);
						SUPER::setVisible(false);
						animationType = WINDOW_ANIMATION_TYPE_CLOSED;
					}
				}
			}
			super::onUpdate(updateInfomation);
		}

		/*!
		 * @brief 描画イベント
		 * @param[in]	drawRect	描画情報
		 */
		virtual bool onDraw(const DrawRect& drawRect)
		{
			const Vector2 s = drawRect.getDrawSize() * 0.5f;
			const Vector2 p = drawRect.getDrawLeftTopPosition() + s - s * animationWindowScale;

			DrawRect newDrawRect(drawRect);
			newDrawRect.position = p;
			newDrawRect.scale = drawRect.scale * animationWindowScale;
			newDrawRect.color = drawRect.color * animationWindowColor;
			newDrawRect.updateClippingInfomation(drawRect);

			return super::onDraw(newDrawRect);
		}

		/*!
		 * @brief		タッチ開始イベント
		 * @param[in]	localPosition	ローカル座標
		 * @return		イベント完了フラグ。trueを返すと他のコンポーネントにイベントが発生しません
		 */
		virtual bool touchesBegan(const Vector2& localPosition)
		{
			return isAnimationStoped() ? super::touchesBegan(localPosition) : false;
		}

		/*!
		 * @brief		タッチ移動イベント
		 * @param[in]	localPosition	ローカル座標
		 * @return		イベント完了フラグ。trueを返すと他のコンポーネントにイベントが発生しません
		 */
		virtual bool touchesMoved(const Vector2& localPosition)
		{
			return isAnimationStoped() ? super::touchesMoved(localPosition) : false;
		}

		/*!
		 * @brief		タッチ終了イベント
		 * @param[in]	localPosition	ローカル座標
		 * @return		イベント完了フラグ。trueを返すと他のコンポーネントにイベントが発生しません
		 */
		virtual bool touchesEnded(const Vector2& localPosition)
		{
			return isAnimationStoped() ? super::touchesEnded(localPosition) : false;
		}

		/*!
		 * @brief タッチキャンセルイベント
		 * @param[in]	localPosition	ローカル座標
		 * @return		イベント完了フラグ。trueを返すと他のコンポーネントにイベントが発生しません
		 */
		virtual bool touchesCancelled(const Vector2& localPosition)
		{
			return isAnimationStoped() ? super::touchesCancelled(localPosition) : false;
		}

		/*!
		 * @brief		同じメニューか比較
		 * @param[in]	other	比較するウィンドウ(Window)オブジェクト
		 */
		virtual bool compare(const Window& window) const
		{
			if(!super::compare(window))
				return false;
			if(animationType != window.animationType)
				return false;
			if(animationWindowScale != window.animationWindowScale)
				return false;
			if(animationWindowColor != window.animationWindowColor)
				return false;
			return true;
		}

		/*!
		 * @brief	ウィンドウアニメーション時間を取得
		 * @return	ウィンドウアニメーション時間
		 */
		static float getAnimationTime()
		{
			return 0.25f;
		}

	private:
		WindowAnimationType animationType;		//!< @brief アニメーション状態
		float animationWindowTime;				//!< @brief アニメーション時間
		Color animationWindowColor;				//!< @brief 色
		Vector2 animationWindowScale;			//!< @brief スケール
	};
}

#endif
